﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

using System.IO;

using System.Diagnostics;

namespace WindowsFormsApplication3
{
    class SkinColorAreaDatabase
    {
        public const double MAX_CB = 255 * (1 - 0.114);
        public const double MAX_CR = 255 * (1 - 0.299);

        public const double MIN_CB = 255 * -(0.587 + 0.299);
        public const double MIN_CR = 255 * -(0.587 + 0.114);

        private List<Rectangle> skinColorAreas; //list with rectangles containing skin pixels in Cb-Cr space with coordinates starting at zero and y pointing down
        
        public SkinColorAreaDatabase()
        {
            skinColorAreas = new List<Rectangle>();
        }

        public Boolean IsSkinPixel(Color color)
        {
            double[] yCbCrColor = ConvertColorToYCbCr(color);

            //convert to coordinates
            int x = (int)(yCbCrColor[1] - MIN_CB);
            int y = (int)(yCbCrColor[2] - MIN_CR);
            //int y = (int)(yCbCrColor[2] - MAX_CB);

            foreach (Rectangle skinColorArea in skinColorAreas)
            {
                if (Intersects(new Point(x, y), skinColorArea)) return true;
            }

            return false;
        }

        public static Boolean Intersects(Point point, Rectangle rect)
        {
            if (point.X >= rect.X && point.X < rect.X + rect.Width && point.Y >= rect.Y && point.Y < rect.Y + rect.Height)
                return true;
            else
                return false;
        }

        public static double[] ConvertColorToYCbCr(Color color)
        {
            int r = color.R;
            int g = color.G;
            int b = color.B;

            double y = 0.299 * r + 0.587 * g + 0.114 * b;
            double cb = b - y;  //between 0.886*255 and -255*2
            double cr = r - y;  //between 0.701*255 and -255*2

            return new double[] { y, cb, cr };
        }

        public void LoadAreasFromFile(string FileName)
        {
            FileStream fs = new FileStream(FileName, FileMode.Open, FileAccess.Read);
            BinaryReader br = new BinaryReader(fs);
            int nrOfElements = br.ReadInt32();
            for (int i = 0; i < nrOfElements; i++)
            {
                int x = br.ReadInt32();
                int y = br.ReadInt32();
                int width = br.ReadInt32();
                int height = br.ReadInt32();

                skinColorAreas.Add(new Rectangle(x, y, width, height));
            }
            br.Close();
            fs.Close();
        }

        public void SaveAreasToFile(string FileName)
        {
            FileStream fs = new FileStream(FileName, FileMode.Create, FileAccess.Write);
            BinaryWriter bw = new BinaryWriter(fs);
            bw.Write(skinColorAreas.Count);
            foreach (Rectangle rect in skinColorAreas)
            {
                bw.Write(rect.X);
                bw.Write(rect.Y);
                bw.Write(rect.Width);
                bw.Write(rect.Height);
            }
            bw.Close();
            fs.Close();

            //also save text backup
            SaveAreasToTextFile(FileName);
        }

        //saves a text file displaying the rules
        private void SaveAreasToTextFile(string sFileName)
        {
            StreamWriter swWriter;
            swWriter = File.CreateText(sFileName + ".txt");
            swWriter.WriteLine("X is Cb, Y is Cr pointing down. 0,0 denotes the minimum value of Cb,Cr respectively");

            foreach (Rectangle rect in skinColorAreas)
            {
                swWriter.WriteLine(rect.X + ", " + rect.Y + ", " + rect.Width + ", " + rect.Height);
            }

            swWriter.Close();
        }

        public List<Rectangle> SkinColorAreas
        {
            get
            {
                return skinColorAreas;
            }
            set
            {
                skinColorAreas = value;
            }
        }
    }
}
